﻿using System;
using System.IO;

namespace Narwhunderful.Aphelia.Framework.IO.Serialization
{
    public abstract class PropertySerializer : IPropertySerializer
    {

        public void Serialize(Property property)
        {
            SerializeCore(new PropertyTypeInfo<Property>(property, null));
        }
        public abstract void Open(Stream stream);
        public abstract void Close();

        protected void SerializeCore(PropertyTypeInfo<Property> property)
        {
            if (property == null) throw new ArgumentNullException("property");

            var nullProperty = property.Property as NullProperty;
            if (nullProperty != null)
            {
                SerializeNullProperty(new PropertyTypeInfo<NullProperty>(nullProperty, property.ExpectedPropertyType,
                                                                         property.ValueType));
                return;
            }

            // check if the value type is equal to the property type.
            // if so, there is no need to explicit define the value type
            if (property.ExpectedPropertyType != null && property.ExpectedPropertyType == property.ValueType)
            {
                // Type is not required, because the property has the same value type as the expected property type
                property.ValueType = null;
            }

            var simpleProperty = property.Property as SimpleProperty;
            if (simpleProperty != null)
            {
                SerializeSimpleProperty(new PropertyTypeInfo<SimpleProperty>(simpleProperty,
                                                                             property.ExpectedPropertyType,
                                                                             property.ValueType));
                return;
            }

            var multiDimensionalArrayProperty = property.Property as MultiDimensionalArrayProperty;
            if (multiDimensionalArrayProperty != null)
            {
                SerializeMultiDimensionalArrayProperty(
                    new PropertyTypeInfo<MultiDimensionalArrayProperty>(multiDimensionalArrayProperty,
                                                                        property.ExpectedPropertyType,
                                                                        property.ValueType));
                return;
            }

            var singleDimensionalArrayProperty = property.Property as SingleDimensionalArrayProperty;
            if (singleDimensionalArrayProperty != null)
            {
                SerializeSingleDimensionalArrayProperty(
                    new PropertyTypeInfo<SingleDimensionalArrayProperty>(singleDimensionalArrayProperty,
                                                                         property.ExpectedPropertyType,
                                                                         property.ValueType));
                return;
            }

            var dictionaryProperty = property.Property as DictionaryProperty;
            if (dictionaryProperty != null)
            {
                SerializeDictionaryProperty(new PropertyTypeInfo<DictionaryProperty>(dictionaryProperty,
                                                                                     property.ExpectedPropertyType,
                                                                                     property.ValueType));
                return;
            }

            var collectionProperty = property.Property as CollectionProperty;
            if (collectionProperty != null)
            {
                SerializeCollectionProperty(new PropertyTypeInfo<CollectionProperty>(collectionProperty,
                                                                                     property.ExpectedPropertyType,
                                                                                     property.ValueType));
                return;
            }

            var complexProperty = property.Property as ComplexProperty;
            if (complexProperty != null)
            {
                SerializeComplexProperty(new PropertyTypeInfo<ComplexProperty>(complexProperty,
                                                                               property.ExpectedPropertyType,
                                                                               property.ValueType));
                return;
            }

            var complexReferenceProperty = property.Property as ComplexReferenceProperty;
            if (complexReferenceProperty != null)
            {
                SerializeComplexReferenceProperty(new PropertyTypeInfo<ComplexReferenceProperty>(complexReferenceProperty,
                                                                               null,
                                                                               null));
                return;
            }

            throw new InvalidOperationException(string.Format("Unknown Property: {0}", property.Property.GetType()));
        }

        protected abstract void SerializeNullProperty(PropertyTypeInfo<NullProperty> property);
        protected abstract void SerializeSimpleProperty(PropertyTypeInfo<SimpleProperty> property);
        protected abstract void SerializeMultiDimensionalArrayProperty(PropertyTypeInfo<MultiDimensionalArrayProperty> property);
        protected abstract void SerializeSingleDimensionalArrayProperty(PropertyTypeInfo<SingleDimensionalArrayProperty> property);
        protected abstract void SerializeDictionaryProperty(PropertyTypeInfo<DictionaryProperty> property);
        protected abstract void SerializeCollectionProperty(PropertyTypeInfo<CollectionProperty> property);
        protected abstract void SerializeComplexProperty(PropertyTypeInfo<ComplexProperty> property);
        protected abstract void SerializeComplexReferenceProperty(PropertyTypeInfo<ComplexReferenceProperty> property);

    }

}
